package net.seratch.taskun.scheduler.impl;

import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import net.seratch.taskun.Scheduler;
import net.seratch.taskun.common.ComponentContainerAdaptor;
import net.seratch.taskun.scheduler.crond.CronDaemon;
import net.seratch.taskun.scheduler.util.SchedulerUtil;
import net.seratch.taskun.util.CalendarUtil;

/**
 * Taskun's basic scheduler implementation
 * 
 * @author Kazuhiro Sera
 */
public class TaskunScheduler implements Scheduler {

	/**
	 * Executor Service
	 */
	private ScheduledExecutorService executorService;

	/**
	 * Component container adaptor
	 */
	private ComponentContainerAdaptor containerAdaptor;

	/**
	 * Cron daemon
	 */
	private CronDaemon crond = new CronDaemon();

	/**
	 * Crontab filepath
	 */
	private String crontabFile;

	public void initialize(ComponentContainerAdaptor containerAdaptor) {
		this.containerAdaptor = containerAdaptor;
		executorService = Executors.newScheduledThreadPool(3,
				new ThreadFactory() {
					private ThreadGroup threadGroup = new ThreadGroup(
							"taskun-scheduler");

					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(threadGroup, r);
						t.setDaemon(true);
						t.setName(t.getThreadGroup().getName() + "-worker-"
								+ t.getId());
						return t;
					}
				});
	}

	@Override
	public void start() {
		invokeCronDaemon(containerAdaptor, executorService);
	}

	@Override
	public void scheduleCronExecute(Runnable runnable, String crontab) {
		crond.addCrontab(crontab);
	}

	@Override
	public void scheduleIntervalExecute(Runnable runnable,
			Calendar initialScheduledDate, long interval, TimeUnit timeUnit) {
		long initialDelay = SchedulerUtil.getDelayValue(initialScheduledDate,
				timeUnit);
		executorService.scheduleAtFixedRate(runnable, initialDelay, interval,
				timeUnit);
	}

	@Override
	public void scheduleOnetime(Runnable runnable, Calendar scheduledDate) {
		Calendar currentDate = CalendarUtil.getCurrentTime();
		long delay = scheduledDate.getTimeInMillis()
				- currentDate.getTimeInMillis();
		executorService.schedule(runnable, delay, TimeUnit.MILLISECONDS);
	}

	@Override
	public void shutdown() {
		executorService.shutdownNow();
	}

	protected void invokeCronDaemon(ComponentContainerAdaptor containerAdaptor,
			ScheduledExecutorService executorService) {
		if (containerAdaptor == null) {
			throw new IllegalStateException(
					"Not initialized scheduler - the containerAdaptor is null");
		}
		if (crontabFile != null) {
			crond.initialize(containerAdaptor, executorService, crontabFile);
		} else {
			crond.initialize(containerAdaptor, executorService);
		}
		Calendar cal = CalendarUtil.getCurrentTime();
		long currentTime = cal.getTimeInMillis();
		cal.add(Calendar.MINUTE, 1);
		cal.set(Calendar.SECOND, 0);
		long initialInvocation = cal.getTimeInMillis();
		long initialDelay = initialInvocation - currentTime;
		executorService.scheduleAtFixedRate(crond, initialDelay, 60000L,
				TimeUnit.MILLISECONDS);
	}

	@Override
	public void replaceCrontabFile(String crontabFile) {
		this.crontabFile = crontabFile;
	}

	@Override
	public boolean isRunning() {
		return (executorService == null || executorService.isTerminated()) ? false
				: true;
	}

	@Override
	public List<String> getCurrentSchedulingList() {
		return crond.getSchedulingStringList();
	}

}
